package com.springsource.boot.run;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.*;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;

import java.util.List;
import java.util.Set;

@Slf4j
public class SpringbootRun2_8_9_10_11_12 {

    public static void main(String[] args) throws Exception {
        SpringApplication app = new SpringApplication();
        app.addInitializers(context -> {
            log.warn("执行初始化器增强……");
        });

        log.error(">>>>>>>>>>>>>>>>> 2. 封装启动Args");
        DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);

        log.error(">>>>>>>>>>>>>>>>> 8. 创建容器");
        GenericApplicationContext context = createApplicationContext(WebApplicationType.SERVLET);

        log.error(">>>>>>>>>>>>>>>>>9. 准备容器");
        for (ApplicationContextInitializer initializer : app.getInitializers()) {
            initializer.initialize(context);
        }

        log.error(">>>>>>>>>>>>>>>>>10. 加载bean定义");
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
        // 加载config类中的bean
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
        reader.register(Config.class);
        // 加载xml中的bean
        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(beanFactory);
        xmlReader.loadBeanDefinitions("classpath:b06.xml");
        // 加载注解注册的bean（Component、Service、Controller）
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
        scanner.scan("com.springsource.boot.sub");
        // 上边方法中的配置类、配置文件和包，都是从app中的Sources中取的
//        app.setSources();


        log.error(">>>>>>>>>>>>>>>>>11. refresh容器");
        context.refresh();
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            log.warn("beanName: {}  beanResource:{}", beanDefinitionName, beanFactory.getBeanDefinition(beanDefinitionName).getResourceDescription());
        }

        log.error(">>>>>>>>>>>>>>>>>12. 执行runner");
        // 什么是runner，是实现了特定接口的bean，有run方法
        // 两个接口是CommandLineRunner、ApplicationRunner，区别在于run方法的参数形式不同
        for (CommandLineRunner value : context.getBeansOfType(CommandLineRunner.class).values()) {
            value.run(args);
        }
        for (ApplicationRunner value : context.getBeansOfType(ApplicationRunner.class).values()) {
            value.run(arguments);
        }


    }


    private static GenericApplicationContext createApplicationContext(WebApplicationType type) {
        GenericApplicationContext context = null;
        switch (type) {
            case SERVLET -> context = new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE -> context = new AnnotationConfigReactiveWebServerApplicationContext();
            case NONE -> context = new AnnotationConfigApplicationContext();
        }
        return context;
    }

    static class Bean4 {}

    static class Bean5 {}

    static class Bean6 {}

    @Configuration
    static class Config{
        @Bean
        public Bean5 bean5(){
            return new Bean5();
        }

        @Bean
        public ServletWebServerFactory servletWebServerFactory(){
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public CommandLineRunner commandLineRunner(){
            return args -> log.warn("执行commandRunner");
        }

        @Bean
        public ApplicationRunner applicationRunner(){
            return new ApplicationRunner() {
                @Override
                public void run(ApplicationArguments args) throws Exception {
                    List<String> nonOptionArgs = args.getNonOptionArgs();
                    Set<String> optionNames = args.getOptionNames();
                    for (String optionName : optionNames) {
                        args.getOptionValues(optionName);
                    }
                    log.warn("执行applicationRunner");
                }
            };
        }
    }



}
